package com.gzz.orchid.common.webmvc.support.vistlog;


import com.alibaba.fastjson2.JSON;
import com.gzz.orchid.common.core.util.IPUtil;
import com.gzz.orchid.common.core.util.crypt.AOPUtil;
import com.gzz.orchid.common.webmvc.util.ReqUtil;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.core.annotation.Order;
import org.springframework.util.StopWatch;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.time.LocalDateTime;
import java.util.Map;
import java.util.Objects;

/**
 * @author higmd
 * 访问日志切面 处理
 */
@Slf4j
@Aspect
@Order(2)
public class VisitLogAspect {
    // 慢查询值
    private static final int SLOW_QUERY = 100;
    @Autowired
    private ApplicationEventPublisher eventPublisher;

    /**
     * 注解-方法切口
     */
    @Around("@annotation(com.gzz.orchid.common.webmvc.support.vistlog.VisitLog)")
    public Object doAnnontationMethod(ProceedingJoinPoint pjp) throws Throwable {
        // System.out.println("=切点==============@annotation");
        return executeWithLogHandle(pjp);
    }

    /**
     * 注解-类切口
     */
    @Around("@within(com.gzz.orchid.common.webmvc.support.vistlog.VisitLog)")
    public Object doAnnontationClass(ProceedingJoinPoint pjp) throws Throwable {
        return executeWithLogHandle(pjp);
    }

    /**
     * 执行切面的处理
     */
    private Object executeWithLogHandle(ProceedingJoinPoint joinPoint) throws Throwable {
        //  Signature signature = joinPoint.getSignature();
        //  MethodSignature methodSignature = (MethodSignature) signature;
        //  Method targetMethod = methodSignature.getMethod();
        String name = joinPoint.getSignature().getName();
        // 接收到请求，记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        //发布日志事件
        VisitLogEvent evt = new VisitLogEvent();
        evt.setVisitTime(LocalDateTime.now());
        evt.setVisitUrl(request.getRequestURL().toString());
        evt.setVisitMethod(request.getMethod());
        evt.setVisitParams(formatArgs(joinPoint.getArgs()));
        evt.setVisitIpaddr(IPUtil.getIpAddress(request));
        evt.setBrowser(ReqUtil.getUserAgent());
        evt.setExecMethod(joinPoint.getSignature().getDeclaringTypeName() + "." + name);
        // 定义结果
        Object result = null;
        StopWatch watch = new StopWatch();
        watch.start();
        try {
            //处理App 切入参数
            VisitLog visitLog = AOPUtil.getAnnotationPresent(joinPoint, VisitLog.class);
            evt.setDigest(visitLog.digest());
            result = joinPoint.proceed();
            if (Objects.nonNull(result)) {
                evt.setResultBody(JSON.toJSONString(result));
            } else {
                evt.setResultBody("==");
            }
        } catch (Throwable e) {
            log.error("执行方法----发生异常了。。。。{}", stackTraceToString(e));
            e.printStackTrace();
            evt.setIsException(true);
            evt.setExceptionBody(e.getMessage());

            //再次执行 让外面执行异常。
            result = joinPoint.proceed();
        } finally {
            watch.stop();
            evt.setUsedTime(watch.getTotalTimeMillis());
            evt.setIsSlowRequest(isSlowRequest(evt.getUsedTime()));
            // 发布事件
            eventPublisher.publishEvent(evt);
        }
        return result;
    }


    /**
     * 转换异常信息为字符串
     *
     * @param exception 异常
     */
    private String stackTraceToString(Throwable exception) {
        // 异常名称
        String exceptionName = exception.getClass().getName();
        // 异常信息
        String exceptionMessage = exception.getMessage();
        // 堆栈信息
        StackTraceElement[] elements = exception.getStackTrace();

        StringBuffer strbuff = new StringBuffer();
        for (StackTraceElement stet : elements) {
            strbuff.append(stet + "\n");
        }
        String message = exceptionName + ":" + exceptionMessage + "\n\t" + strbuff;
        return message;
    }

    /**
     * 格式化输入参数
     */
    private String formatArgs(Object[] args) {
        if (null != args && args.length > 0) {
            StringBuffer sb = new StringBuffer();
            int i = 0;
            for (Object arg : args) {
                sb.append(String.format("| args[%d]: ", i++));
                if (arg instanceof HttpServletRequest) {
                    HttpServletRequest request = (HttpServletRequest) arg;
                    Map<String, String[]> paramMap = request.getParameterMap();
                    sb.append(JSON.toJSONString(paramMap));
                } else {
                    sb.append(JSON.toJSONString(arg));
                }
            }
            return sb.toString();
        } else {
            return "--";
        }
    }

    /**
     * 判断是否慢请求
     *
     * @param execTime 执行时间
     * @return
     */
    private boolean isSlowRequest(long execTime) {
        return execTime > SLOW_QUERY;
    }
}